package com.stone.serverless.web;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.*;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Set;

/**
 * @Description: TODO
 * @Author: MECHREVO
 * @Date: 2022/1/25 16:25
 * @Version: 1.0.0
 */
public class ClassLoaderRequestMappingInfoHandlerMapping extends RequestMappingHandlerMapping {
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    public ClassLoaderRequestMappingInfoHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping) {
        this.requestMappingHandlerMapping = requestMappingHandlerMapping;
        this.init();
    }

    public void init() {
        Field[] fields = ReflectUtil.getFields(requestMappingHandlerMapping.getClass());

        for (Field f : fields) {
            if (Modifier.isStatic(f.getModifiers())) {
                continue;
            }
            Object fieldValue = ReflectUtil.getFieldValue(requestMappingHandlerMapping, f);
            if (ObjectUtil.isNotNull(fieldValue)) {
                ReflectUtil.setFieldValue(this, f, fieldValue);
            }
        }
    }

    @Override
    protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {


        RequestMappingInfo matchingMapping = super.getMatchingMapping(info, request);
        if (ObjectUtil.isNull(matchingMapping)) {
            RequestMappingInfo o = this.matchingCondition(info, request);
            return o;
        }

        return matchingMapping;
    }

    public RequestMappingInfo matchingCondition(RequestMappingInfo info, HttpServletRequest request) {
        RequestMethodsRequestCondition methods = ReflectUtil.invoke(ReflectUtil.getFieldValue(info, "methodsCondition"),
                "getMatchingCondition", request);
        if (methods == null) {
            return null;
        }
        ParamsRequestCondition params =
                ReflectUtil.invoke(ReflectUtil.getFieldValue(info, "paramsCondition"),
                        "getMatchingCondition", request);
        if (params == null) {
            return null;
        }
        HeadersRequestCondition headers =
                ReflectUtil.invoke(ReflectUtil.getFieldValue(info, "headersCondition"),
                        "getMatchingCondition", request);
        if (headers == null) {
            return null;
        }
        ConsumesRequestCondition consumes =
                ReflectUtil.invoke(ReflectUtil.getFieldValue(info, "consumesCondition"),
                        "getMatchingCondition", request);
        if (consumes == null) {
            return null;
        }
        ProducesRequestCondition produces =
                ReflectUtil.invoke(ReflectUtil.getFieldValue(info, "producesCondition"),
                        "getMatchingCondition", request);
        if (produces == null) {
            return null;
        }


        String requestURI = request.getRequestURI();
        String[] split = requestURI.split("/");
        if (split.length != 4) {
            return null;
        }

        PatternsRequestCondition patternsCondition = info.getPatternsCondition();
        if (ObjectUtil.isNull(patternsCondition)) {
            return null;
        }
        Set<String> patterns = patternsCondition.getPatterns();

        if (patterns.size() <= 0) {
            return null;
        }
        Boolean re = false;
        for (String ss : patterns) {
            String[] split1 = ss.split("/");
            if (split.length < 2) {
                break;
            }
            if (split1[0].equals(split[0]) && split1[1].equals(split[1])) {
                re = true;
                break;
            }

        }
        if (!re) {
            return null;
        }


        Object mr = ReflectUtil.getFieldValue(this, "mappingRegistry");

        if (ObjectUtil.isNull(mr)) {
            return null;
        }
        Object getRegistrations = ReflectUtil.invoke(mr, "getRegistrations");
        if (ObjectUtil.isNull(getRegistrations)) {
            return null;
        }
        Map map = (Map) getRegistrations;
        Object o = map.get(info);
        if (ObjectUtil.isNull(o)) {
            return null;
        }
        Object hm = ReflectUtil.getFieldValue(o, "handlerMethod");
        if (ObjectUtil.isNull(hm)) {
            return null;
        }
        HandlerMethod handlerMethod = (HandlerMethod) hm;
        String me = handlerMethod.getBean().getClass().getName();
        if (!(split[2].equals(me) && handlerMethod.getMethod().getName().equals(split[3]))) {
            return null;
        }

        RequestConditionHolder custom =
                ReflectUtil.invoke(ReflectUtil.getFieldValue(info, "customConditionHolder"),
                        "getMatchingCondition", request);
        if (custom == null) {
            return null;
        }
        String name = (String) ReflectUtil.getFieldValue(info, "name");
        RequestMappingInfo.BuilderConfiguration options = (RequestMappingInfo.BuilderConfiguration) ReflectUtil.getFieldValue(info, "options");

        RequestMappingInfo requestMappingInfo = ReflectUtil.newInstance(RequestMappingInfo.class,
                name, info.getPathPatternsCondition(), info.getPatternsCondition(),
                methods, params, headers, consumes, produces, custom, options);
        return requestMappingInfo;

    }
}
